package com.cloudkinto.service.statistics.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cloudkinto.common.DicBusinessConstant;
import com.cloudkinto.common.DicBusinessItemConstant;
import com.cloudkinto.common.common.DateUtils;
import com.cloudkinto.common.utils.TimeUtils;
import com.cloudkinto.common.web.PageResult;
import com.cloudkinto.common.web.SingleResult;
import com.cloudkinto.dao.*;
import com.cloudkinto.entity.*;
import com.cloudkinto.service.dic.DicBusinessItemService;
import com.cloudkinto.service.dic.vo.DicItemRes;
import com.cloudkinto.service.finance.vo.IdAndNameEntity;
import com.cloudkinto.service.statistics.StatisticsService;
import com.cloudkinto.service.statistics.vo.RecordPageRes;
import com.cloudkinto.service.statistics.vo.StatisticsRes;
import com.cloudkinto.service.statistics.vo.StatisticsShopCountRes;
import com.cloudkinto.service.stock.StockRecordService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author zhangyy
 * @date 2020/6/19 9:26
 * @description:
 */
@Service
public class StatisticsServiceImpl implements StatisticsService {

    @Autowired
    private OrderInputDao orderInputDao;
    @Autowired
    private OrderOutputDao orderOutputDao;
    @Autowired
    private StockRecordDao stockRecordDao;
    @Autowired
    private ProductInfoDao productInfoDao;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private UserDao userDao;
    @Autowired
    private StockHistoryDao stockHistoryDao;
    @Autowired
    private StockRecordService stockRecordService;
    @Autowired
    private ShopDao shopDao;
    @Autowired
    private CompanyAccountDao companyAccountDao;
    @Autowired
    private CompanyDao companyDao;
    @Autowired
    private OrderBackDao backOrderDao;
    @Autowired
    private JpRegionDao jpRegionDao;
    @Autowired
    private DicBusinessItemDao dicBusinessItemDao;
    @Autowired
    private DicBusinessItemService dicBusinessItemService;
    @Autowired
    private CostRecordDao costRecordDao;
    @Autowired
    private ExchangeRateDao exchangeRateDao;


    @Override
    public SingleResult statistics(Long userId, Long companyId) {
        StatisticsRes res = new StatisticsRes();

        UserDo userDo = userDao.selectById(userId);

        //1
        JSONObject yesterdayDelivery = yesterdayDelivery(userDo, companyId);
        res.setSalesOfMonth(yesterdayDelivery);

        //2
        JSONObject todayStock = todayStock(userDo, companyId);
        res.setStockFeeOfMonth(todayStock);

        //3
        JSONObject stockTotalQuantity = stockTotalQuantity(userDo, companyId);
        res.setStockInput(stockTotalQuantity);

        //4
        JSONObject yesterdayDeliveryFee = yesterdayDeliveryFee(userDo, companyId);
        res.setCompleteRate(yesterdayDeliveryFee);

        return SingleResult.success(res);
    }


    @Override
    public SingleResult getCompanyAccount(Long userId, Long companyId) {
        JSONObject jsonObject = new JSONObject();
        LambdaQueryWrapper<CompanyAccountDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CompanyAccountDo::getCompanyId, companyId);
        CompanyAccountDo companyAccountDo = companyAccountDao.selectOne(wrapper);
        if (companyAccountDo != null) {
            jsonObject.put("companyAccount", companyAccountDo.getCompanyAccount());
            ExchangeRateDo exchangeRate = exchangeRateDao.getJPRate();
            jsonObject.put("companyAccountRmb", new BigDecimal(companyAccountDo.getCompanyAccount()  * exchangeRate.getRate()).setScale(0, RoundingMode.HALF_UP).toPlainString());
        }
        CompanyDo companyDo = companyDao.selectById(companyId);
        if (companyDo != null) {
            jsonObject.put("companyCode", companyDo.getCompanyCode());
        }

        return SingleResult.success(jsonObject);
    }

    @Override
    public PageResult getListPage(Map<String, String> map) {
        IPage<OrderProductSellCountEntity> page = pageInit(map);
        List<OrderProductSellCountEntity> productSellCountEntities = orderOutputDao.selectBastSellProduct(page, map);
        PageResult result = new PageResult(productSellCountEntities, (int) page.getCurrent(), (int) page.getSize(), (int) page.getTotal(), (int) page.getPages());
        return result;
    }

    //查询分页列表之后对列表数据的处理
    private List<RecordPageRes> handlerListAfter(List<StockRecordDo> list) {
        List<RecordPageRes> result = new ArrayList<>();
        if (!CollectionUtils.isEmpty(list)) {
            list.forEach(stockRecordDo -> {
                RecordPageRes res = new RecordPageRes();
                res.setId(stockRecordDo.getProductInfoId());
                res.setCount(stockRecordDo.getCanSellCount());
                ShopDo shopDo = shopDao.selectById(stockRecordDo.getShopId());
                if (shopDo != null) {
                    res.setShopName(shopDo.getShopName());
                }
                ProductInfoDo productInfoDo = productInfoDao.selectById(stockRecordDo.getProductInfoId());
                if (productInfoDo != null) {
                    res.setCommoditySku(productInfoDo.getCommoditySku());
                }
                result.add(res);
            });
        }
        return result;
    }

    private QueryWrapper<StockRecordDo> queryBuild(Map<String, String> map) {
        QueryWrapper<StockRecordDo> wrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(map.get("companyId"))) {
            wrapper.eq("company_id", map.get("companyId"));
        }
        if (StringUtils.isNotBlank(map.get("shopId"))) {
            wrapper.eq("shop_id", map.get("shopId"));
        }
        wrapper.lt("can_sell_count", 50);
        wrapper.orderByDesc("id");
        return wrapper;
    }

    private IPage<OrderProductSellCountEntity> pageInit(Map<String, String> map) {
        IPage<OrderProductSellCountEntity> page = new Page();
        page.setSize(StringUtils.isNotBlank(map.get("pageSize")) ? Long.parseLong(map.get("pageSize")) : 10);
        page.setCurrent(StringUtils.isNotBlank(map.get("current")) ? Long.parseLong(map.get("current")) : 1);
        return page;
    }


    private JSONObject yesterdayDelivery(UserDo userDo, Long companyId) {
        JSONObject jsonObject = new JSONObject();

        Date fistDayOfMonth = DateUtils.getEndDayOrOneDay(new Date(), 0, 0);
        Date lastDayOfMonth = DateUtils.getEndDayOrOneDay(new Date(), 1, 0);
        Date fistDayOfLastMonth = DateUtils.getEndDayOrOneDay(new Date(), 0, -1);
        Date lastDayOfLastMonth = DateUtils.getEndDayOrOneDay(new Date(), 1, -1);
        Date yestday = DateUtils.passingDate(new Date(), -1);

        //本月总销量
        LambdaQueryWrapper<OrderOutputDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(OrderOutputDo::getSendDate, TimeUtils.formatDate(fistDayOfMonth))
                .le(OrderOutputDo::getSendDate, TimeUtils.formatDate(lastDayOfMonth))
                .eq(OrderOutputDo::getCompanyId, companyId);
        if (userDo != null && userDo.getShopId() != null) {
            wrapper.eq(OrderOutputDo::getShopId, userDo.getShopId());
        }
        int countOfMonth = orderOutputDao.selectCount(wrapper);
        //上月总销量
        wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(OrderOutputDo::getSendDate, TimeUtils.formatDate(fistDayOfLastMonth))
                .le(OrderOutputDo::getSendDate, TimeUtils.formatDate(lastDayOfLastMonth))
                .eq(OrderOutputDo::getCompanyId, companyId);
        if (userDo != null && userDo.getShopId() != null) {
            wrapper.eq(OrderOutputDo::getShopId, userDo.getShopId());
        }
        int countOfLastMonth = orderOutputDao.selectCount(wrapper);
        jsonObject.put("countOfMonth", countOfMonth);
        // TODO: 2021/6/17 亚马逊AFN订单数计算 Order_status=Shipped，fmc=AFN
        //昨日销量
        wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderOutputDo::getSendDate, TimeUtils.formatDate(yestday))
                .eq(OrderOutputDo::getCompanyId, companyId);
        if (userDo != null && userDo.getShopId() != null) {
            wrapper.eq(OrderOutputDo::getShopId, userDo.getShopId());
        }
        int countOfYesterday = orderOutputDao.selectCount(wrapper);
        //今日销量
        LambdaQueryWrapper<OrderOutputDo> todaywrapper = new LambdaQueryWrapper<>();
        todaywrapper.eq(OrderOutputDo::getSendDate, TimeUtils.getCurrentDateFormat())
                .eq(OrderOutputDo::getCompanyId, companyId);
        if (userDo != null && userDo.getShopId() != null) {
            todaywrapper.eq(OrderOutputDo::getShopId, userDo.getShopId());
        }
        int countOfToday = orderOutputDao.selectCount(todaywrapper);
        jsonObject.put("countOfYesterday", countOfYesterday);
        jsonObject.put("dayRate", countOfYesterday == 0 ? 0 : numberFormat(Double.valueOf(countOfToday - countOfYesterday) / countOfYesterday));
        jsonObject.put("MonthRate", countOfLastMonth == 0 ? 0 : numberFormat(Double.valueOf(countOfMonth - countOfLastMonth) / countOfLastMonth));

        return jsonObject;
    }


    private JSONObject todayStock(UserDo userDo, Long companyId) {
        JSONObject jsonObject = new JSONObject();

        Date fistDayOfMonth = DateUtils.passingDate(new Date(), -30);
        List<Integer> costList;
        Map wrapper = new HashMap();
        wrapper.put("fistDayOfMonth", fistDayOfMonth);
        wrapper.put("lastDayOfMonth", new Date());
        if (userDo != null && userDo.getShopId() != null) {
            wrapper.put("shopId", userDo.getShopId());
            costList = stockHistoryDao.selectFeeOfLast30DaysByShop(wrapper);
        } else {
            wrapper.put("companyId", companyId);
            costList = stockHistoryDao.selectFeeOfLast30DaysByCompany(wrapper);
        }
        if (!CollectionUtils.isEmpty(costList)) {

            double totalFee = costList.stream().mapToInt(Integer::intValue).sum();
            jsonObject.put("totalFee", (int) numberFormat(totalFee));
            jsonObject.put("feeList", costList);
        } else {
            jsonObject.put("feeList", new ArrayList<>());
            jsonObject.put("totalFee", 0);
        }
        return jsonObject;
    }


    private JSONObject stockTotalQuantity(UserDo userDo, Long companyId) {
        JSONObject jsonObject = new JSONObject();
        Date fistDayOfMonth = DateUtils.passingDate(new Date(), -10);
        Map<String, Integer> dateMap = new HashMap<>();
        for (int i = 10; i >= 0; i--) {
            dateMap.put(TimeUtils.formatDate(DateUtils.passingDate(new Date(), -i)), 0);
        }
        Map wrapper = new HashMap();
        List<OrderInputStatisticsEntity> orderInputDoList;
        wrapper.put("inputTime", TimeUtils.formatDate(fistDayOfMonth));
        wrapper.put("companyId", companyId);
        wrapper.put("status", DicBusinessItemConstant.putaway);
        if (userDo != null && userDo.getShopId() != null) {
            wrapper.put("shopId", userDo.getShopId());
            orderInputDoList = orderInputDao.selectInputCountByShopId(wrapper);
        } else {
            wrapper.put("companyId", companyId);
            orderInputDoList = orderInputDao.selectInputCountByCompanyId(wrapper);
        }
        List<Integer> couts = new ArrayList<>();
        int todayCount = 0;
        if (!CollectionUtils.isEmpty(orderInputDoList)) {

            for (OrderInputStatisticsEntity orderInputStatisticsEntity : orderInputDoList) {
                if (dateMap.get(orderInputStatisticsEntity.getInputDate()) != null) {
                    dateMap.put(orderInputStatisticsEntity.getInputDate(), orderInputStatisticsEntity.getCount());
                }
                if (orderInputStatisticsEntity.getInputDate().equals(TimeUtils.getCurrentDateFormat())) {
                    todayCount = orderInputStatisticsEntity.getCount();
                }
            }
        }
        for (Map.Entry<String, Integer> stringIntegerEntry : dateMap.entrySet()) {
            couts.add(stringIntegerEntry.getValue());
        }
        jsonObject.put("feeList", couts);
        jsonObject.put("todayCount", todayCount);
        LambdaQueryWrapper<OrderBackDo> orderBackWrapper = new LambdaQueryWrapper<>();
        if (userDo != null && userDo.getShopId() != null) {
            orderBackWrapper.eq(OrderBackDo::getShopId, userDo.getShopId());
        } else {
            orderBackWrapper.eq(OrderBackDo::getCompanyId, companyId);
        }
        orderBackWrapper.ge(OrderBackDo::getCreateTime, DateUtils.getEndDayOrOneDay(new Date(), 0, 0));
        jsonObject.put("backCount", backOrderDao.selectList(orderBackWrapper).size());
        return jsonObject;
    }


    /**
     * 消费情况
     *
     * @param userDo
     * @param companyId
     * @return
     */
    private JSONObject yesterdayDeliveryFee(UserDo userDo, Long companyId) {
        JSONObject jsonObject = new JSONObject();

        Date yeastday = DateUtils.passingDate(new Date(), -1);
        Date lastSevenday = DateUtils.passingDate(new Date(), -8);

        LambdaQueryWrapper<CostRecordDo> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(CostRecordDo::getCreateTime, DateUtils.getStartOfDay(new Date()));
        if (userDo.getShopId() != null) {
            wrapper.eq(CostRecordDo::getShopId, userDo.getShopId());
        }
        if (companyId != null) {
            wrapper.eq(CostRecordDo::getCompanyId, companyId);
        }
        List<CostRecordDo> costRecordDoList = costRecordDao.selectList(wrapper);
        //昨日花费
        wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(CostRecordDo::getCreateTime, DateUtils.getStartOfDay(yeastday))
                .le(CostRecordDo::getCreateTime, DateUtils.getEndOfDay(yeastday));
        if (userDo.getShopId() != null) {
            wrapper.eq(CostRecordDo::getShopId, userDo.getShopId());
        }
        if (companyId != null) {
            wrapper.eq(CostRecordDo::getCompanyId, companyId);
        }
        List<CostRecordDo> yeastdayCostRecordDoList = costRecordDao.selectList(wrapper);
        //最近7日花费
        wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(CostRecordDo::getCreateTime, DateUtils.getStartOfDay(lastSevenday))
                .le(CostRecordDo::getCreateTime, DateUtils.getEndOfDay(yeastday));
        if (userDo.getShopId() != null) {
            wrapper.eq(CostRecordDo::getShopId, userDo.getShopId());
        }
        if (companyId != null) {
            wrapper.eq(CostRecordDo::getCompanyId, companyId);
        }
        List<CostRecordDo> lastSevendayCostRecordList = costRecordDao.selectList(wrapper);
        LambdaQueryWrapper<CompanyAccountDo> companyAccountDoLambdaQueryWrapper = new LambdaQueryWrapper<>();
        companyAccountDoLambdaQueryWrapper.eq(CompanyAccountDo::getCompanyId, companyId);
        CompanyAccountDo accountDo = companyAccountDao.selectOne(companyAccountDoLambdaQueryWrapper);
        double avg = lastSevendayCostRecordList.stream().mapToDouble(CostRecordDo::getCostAmount).sum() / 7;
        if (accountDo != null) {
            if (avg == 0) avg = 1;
            double v = numberFormat((accountDo.getMaxLimit() + accountDo.getCompanyAccount()) / avg);
            if (v < 0) v = 0;
            jsonObject.put("daysAvailable", (int) v > 10 ? 10 : v);
            jsonObject.put("tips", (v > 10 ? "大于10" : (int) v) + "天");
        } else {
            jsonObject.put("daysAvailable", 0);
            jsonObject.put("tips", "0天");
        }
        if (!CollectionUtils.isEmpty(costRecordDoList)) {
            double fee = costRecordDoList.stream().mapToDouble(CostRecordDo::getCostAmount).sum();
            double yeastdayFee = yeastdayCostRecordDoList.stream().mapToDouble(CostRecordDo::getCostAmount).sum();
            jsonObject.put("dayRate", yeastdayFee == 0 ? 1 : numberFormat(Double.valueOf(fee - yeastdayFee) / yeastdayFee));
            jsonObject.put("fee", numberFormat(fee));
        } else {
            jsonObject.put("dayRate", 0);
            jsonObject.put("fee", 0);
        }

        return jsonObject;
    }


    @Override
    public SingleResult salesStatisticsPage(Map<String, String> map) {
        List<OrderProductSellCountEntity> productSellCountEntities = orderOutputDao.selectSellProductStatistics(map);
        List<JpRegionDo> regionDoList = jpRegionDao.selectList(null);
//        LambdaQueryWrapper<DicBusinessItemDo> productWrapper = new LambdaQueryWrapper<>();
//        productWrapper.eq(DicBusinessItemDo::getDicBusinessId, DicBusinessConstant.productType);
        List<DicItemRes> productDoList = dicBusinessItemService.getDicItemList(DicBusinessConstant.productType);
        List<Map> regionList = new ArrayList<>();
        List<Map> productTypeList = new ArrayList<>();
        for (JpRegionDo regionDo : regionDoList) {
            Map regionMap = new HashMap();
            long count = productSellCountEntities.stream().filter(orderProductSellCountEntity -> orderProductSellCountEntity != null && orderProductSellCountEntity.getArea() != null &&
                    orderProductSellCountEntity.getArea().equals(regionDo.getName())).count();
            regionMap.put("id", regionDo.getName());
            regionMap.put("value", count);
            regionList.add(regionMap);
        }
        int totalCount = 0;
        for (DicItemRes dicBusinessItemDo : productDoList) {
            Map productMap = new HashMap();

            long count = productSellCountEntities.stream().filter(orderProductSellCountEntity -> orderProductSellCountEntity != null && orderProductSellCountEntity.getProductType() != null &&
                    Long.parseLong(orderProductSellCountEntity.getProductType()) == dicBusinessItemDo.getId()).count();
            productMap.put("id", dicBusinessItemDo.getValue());
            productMap.put("value", count);
            productTypeList.add(productMap);
            totalCount += count;
        }
        Map result = new HashMap();
        result.put("regionList", regionList);
        result.put("totalCount", totalCount / regionList.size());
        result.put("productType", productTypeList);
        return SingleResult.success(result);
    }

    @Override
    public StatisticsShopCountRes getSalesList(Long shopId, Long companyId) {
        //出货统计
        StatisticsShopCountRes res = sevenDaysDeliveryList(shopId, companyId);
        return res;
    }

    private StatisticsShopCountRes sevenDaysDeliveryList(Long shopId, Long companyId) {
        StatisticsShopCountRes res = new StatisticsShopCountRes();
        List<JSONObject> jsonObjectList = new ArrayList<>();
        List<Map> shopCountList = new ArrayList<>();

        int totalCount = 0;
        for (int i = 1; i <= 7; i++) {
            JSONObject jsonObject = new JSONObject();

            Date date = DateUtils.passingDate(new Date(), -i);
            LambdaQueryWrapper<OrderOutputDo> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(OrderOutputDo::getSendDate, TimeUtils.formatDate(date));
            if (companyId != null) {
                wrapper.eq(OrderOutputDo::getCompanyId, companyId);
            }
            if (shopId != null) {
                wrapper.eq(OrderOutputDo::getShopId, shopId);
            }
            //昨日实际出货数量
            int realCount = orderOutputDao.selectCount(wrapper);
            jsonObject.put("count", realCount);
            totalCount += realCount;

            String format = new SimpleDateFormat("MM-dd").format(date);
            jsonObject.put("date", format);

            jsonObjectList.add(jsonObject);
        }
        if (companyId != null) {
            LambdaQueryWrapper<ShopDo> shopDoLambdaQueryWrapper = new LambdaQueryWrapper<>();
            if (shopId == null) {
                shopDoLambdaQueryWrapper.eq(ShopDo::getCompanyId, companyId);
            } else {
                shopDoLambdaQueryWrapper.eq(ShopDo::getId, shopId);
            }
            List<ShopDo> shopDoList = shopDao.selectList(shopDoLambdaQueryWrapper);
            for (ShopDo shopDo : shopDoList) {
                LambdaQueryWrapper<OrderOutputDo> wrapper = new LambdaQueryWrapper<>();
                wrapper.ge(OrderOutputDo::getSendDate, DateUtils.passingDate(new Date(), -7));
                wrapper.eq(OrderOutputDo::getShopId, shopDo.getId());
                int realCount = orderOutputDao.selectCount(wrapper);
                Map jsonObject = new HashMap<>();
                jsonObject.put("shop", shopDo.getShopName());
                jsonObject.put("cCount", realCount);
                shopCountList.add(jsonObject);
            }
        }
        shopCountList.sort(new Comparator<Map>() {
            @Override
            public int compare(Map o1, Map o2) {
                return (Integer) o2.get("cCount") - (Integer) o1.get("cCount");
            }
        });
        res.setSevenDaysDeliveryTotalCount(totalCount);
        res.setSevenDaysDeliveryList(jsonObjectList);
        res.setSevenDaysDeliveryShopCount(shopCountList);
        return res;
    }


    private List<JSONObject> sevenDaysStockFee(UserDo userDo, Long companyId, StatisticsRes res) {
        List<JSONObject> jsonObjectList = new ArrayList<>();
        double totalFee = 0;
        for (int i = 1; i <= 7; i++) {
            JSONObject jsonObject = new JSONObject();

            Date date = DateUtils.passingDate(new Date(), -i);
            Date dateStart = DateUtils.getStartOfDay(date);
            Date dateEnd = DateUtils.getEndOfDay(date);

            LambdaQueryWrapper<StockHistoryDo> wrapper = new LambdaQueryWrapper<>();
            wrapper.ge(StockHistoryDo::getCreateDate, dateStart)
                    .lt(StockHistoryDo::getCreateDate, dateEnd)
                    .eq(StockHistoryDo::getCompanyId, companyId);
            if (userDo.getShopId() != null) {
                wrapper.eq(StockHistoryDo::getShopId, userDo.getShopId());
            }

            List<StockHistoryDo> stockHistoryDoList = stockHistoryDao.selectList(wrapper);
            if (!CollectionUtils.isEmpty(stockHistoryDoList)) {
                double fee = 0;
                for (StockHistoryDo stockHistoryDo : stockHistoryDoList) {
                    fee += stockHistoryDo.getCost();
                }
                jsonObject.put("fee", numberFormat(fee));
                totalFee += fee;
            } else {
                jsonObject.put("fee", 0);
            }

            String format = new SimpleDateFormat("MM-dd").format(date);
            jsonObject.put("date", format);
            jsonObjectList.add(jsonObject);
        }
        res.setSevenDaysStockTotalFee(numberFormat(totalFee));
        res.setSevenDaysStockFee(jsonObjectList);
        return jsonObjectList;
    }

    private double numberFormat(double f) {
        if (f == Float.NEGATIVE_INFINITY || f == Float.NaN || f == Float.POSITIVE_INFINITY) return 0;
        BigDecimal b = new BigDecimal(f);
        return b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

}